#include "loginRegister.h"
#include <crypt.h>

int user_login(int netfd, MYSQL* mysql, threadPool_t *pThreadPool, UsrInfo_t *UsrInfo)
{
    train_t train;
    while(1)
    {
        while (1)
        {
            char user_name[128] = {0};
            char salt[21] = {0}, encrypt_pws[65] = {0};
            // 接收用户名
            memset(&train, 0, sizeof(train));
            int ret_l = recv(netfd, &train.length, sizeof(train.length), MSG_WAITALL);
            int ret_v = recv(netfd, train.value, train.length, MSG_WAITALL);
            if(ret_l == 0 || ret_v == 0)
            {
                pthread_mutex_lock(&pThreadPool->mutex);
                LOG_INFO(pThreadPool->log_fd, LOG_ERROR, "client disconnected.");
                pthread_mutex_unlock(&pThreadPool->mutex);
                return -1;
            }
            memcpy(user_name, train.value, train.length);

            // 记录登录日志
            pthread_mutex_lock(&pThreadPool->mutex);
            LOG_INFO(pThreadPool->log_fd, LOG_SIGNIN, "user %s try to login.", user_name);
            pthread_mutex_unlock(&pThreadPool->mutex);

            char sql[1024] = {0};
            sprintf(sql, "select salt,encrypted_password from user_info where username = '%s' and is_del = 0;", user_name);

            // 这里需要处理SQL注入攻击，但是目前没有时间，以后的版本再处理(以后应该也没时间)
            int qret = mysql_query(mysql, sql);
            if(qret != 0)
            {
                pthread_mutex_lock(&pThreadPool->mutex);
                LOG_INFO(pThreadPool->log_fd, LOG_ERROR, "mysql_query failed. %s",mysql_error(mysql));
                pthread_mutex_unlock(&pThreadPool->mutex);
                return -1;
            }
            MYSQL_RES *rows = mysql_store_result(mysql);
            // 这里需要判断行数 用户不存在就是0行返回
            // printf("col num = %u, row num = %lu\n", mysql_num_fields(rows), mysql_num_rows(rows));
            if(mysql_num_rows(rows) == 0) // 用户名不存在
            {
                memset(&train, 0, sizeof(train));
                train.type = RESPON_ERROR;
                char *err_msg = "用户名不存在!";
                train.length = strlen(err_msg);
                memcpy(train.value, err_msg, strlen(err_msg));
                send(netfd, &train.type, sizeof(train.type), MSG_NOSIGNAL);
                send(netfd, &train.length, sizeof(train.length), MSG_NOSIGNAL);
                send(netfd, train.value, train.length, MSG_NOSIGNAL);
                continue;
            }
            else
            {
                UsrInfoInit(UsrInfo,user_name); //用户名存在，初始化用户信息

                MYSQL_ROW row = mysql_fetch_row(rows); // 取出一行
                memcpy(salt, row[0], sizeof(salt));               // 获取盐值
                memcpy(encrypt_pws, row[1], sizeof(encrypt_pws)); // 获取密码

                // 发送盐值
                memset(&train, 0, sizeof(train));
                train.type = RESPON_OK;
                send(netfd, &train.type, sizeof(train.type), MSG_NOSIGNAL);
                train.length = strlen(salt);
                send(netfd, &train.length, sizeof(train.length), MSG_NOSIGNAL);
                memcpy(train.value, salt, train.length);
                send(netfd, train.value, train.length, MSG_NOSIGNAL);
            }

            mysql_free_result(rows);

            while (1)
            {
                // 接收客户端密文
                memset(&train, 0, sizeof(train));
                recv(netfd, &train.type, sizeof(train.type), MSG_WAITALL);
                if(train.type == -1)
                {
                    pthread_mutex_lock(&pThreadPool->mutex);
                    LOG_INFO(pThreadPool->log_fd, LOG_ERROR, "client too many incorrect password attempts.");
                    pthread_mutex_unlock(&pThreadPool->mutex);
                    return -1;
                }
                ret_l = recv(netfd, &train.length, sizeof(train.length), MSG_WAITALL);
                ret_v = recv(netfd, train.value, train.length, MSG_WAITALL);
                if (ret_l == 0 || ret_v == 0)
                {
                    pthread_mutex_lock(&pThreadPool->mutex);
                    LOG_INFO(pThreadPool->log_fd, LOG_ERROR, "client disconnected.");
                    pthread_mutex_unlock(&pThreadPool->mutex);
                    return -1;
                }

                if (0 == strcmp(encrypt_pws, train.value))
                {
                    char *success_msg = "login successful.";
                    train.length = strlen(success_msg);
                    train.type = RESPON_OK;
                    send(netfd, &train.type, sizeof(train.type), MSG_NOSIGNAL);
                    memcpy(train.value, success_msg, train.length);
                    send(netfd, &train.length, sizeof(train.length), MSG_NOSIGNAL);
                    send(netfd, train.value, train.length, MSG_NOSIGNAL);
                    // 记录登录成功日志
                    pthread_mutex_lock(&pThreadPool->mutex);
                    LOG_INFO(pThreadPool->log_fd, LOG_SIGNUP, "user %s login successful.", user_name);
                    pthread_mutex_unlock(&pThreadPool->mutex);
                    return 0;
                }
                else
                {
                    char *failed_msg = "wrong password!";
                    train.length = strlen(failed_msg);
                    train.type = RESPON_ERROR;
                    send(netfd, &train.type, sizeof(train.type), MSG_NOSIGNAL);
                    memcpy(train.value, failed_msg, train.length);
                    send(netfd, &train.length, sizeof(train.length), MSG_NOSIGNAL);
                    send(netfd, train.value, train.length, MSG_NOSIGNAL);
                    // 记录登录失败日志
                    pthread_mutex_lock(&pThreadPool->mutex);
                    LOG_INFO(pThreadPool->log_fd, LOG_SIGNIN, "user %s use wrong password.", user_name);
                    pthread_mutex_unlock(&pThreadPool->mutex);
                    continue;
                }
            }
        }
    }  
}

int user_register(int netfd, MYSQL* mysql, threadPool_t *pThreadPool, UsrInfo_t *UsrInfo)
{
    train_t train;

    char user_name[128] = {0};
    //接收用户名
    memset(&train, 0, sizeof(train));
    recv(netfd, &train.length, sizeof(train.length), MSG_WAITALL);
    recv(netfd, user_name, train.length, MSG_WAITALL);

    // 记录注册日志
    pthread_mutex_lock(&pThreadPool->mutex);
    LOG_INFO(pThreadPool->log_fd, LOG_SIGNUP, "user %s try to signup.", user_name);
    pthread_mutex_unlock(&pThreadPool->mutex);

    // 判断用户名是否已存在
    char sql[1024] = {0};
    sprintf(sql, "select username from user_info where username = '%s';", user_name); // 这里账户是否已删除都不允许使用已有的用户名，按照需求可以进行更改
    // 这里需要处理SQL注入攻击，但是目前没有时间，以后的版本再处理(以后应该也没时间)
    int qret = mysql_query(mysql, sql);
    if (qret != 0)
    {
        pthread_mutex_lock(&pThreadPool->mutex);
        LOG_INFO(pThreadPool->log_fd, LOG_ERROR, "mysql_query failed. %s", mysql_error(mysql));
        pthread_mutex_unlock(&pThreadPool->mutex);
        return -1;
    }
    MYSQL_RES *rows = mysql_store_result(mysql);
    // 用户不存在就是0行返回
    if (mysql_num_rows(rows) == 0) // 用户名不存在,OK
    {
        memset(&train, 0, sizeof(train));
        train.type = RESPON_OK;
        char *msg = "可以使用该用户名。";
        train.length = strlen(msg);
        memcpy(train.value, msg, strlen(msg));
        send(netfd, &train.type, sizeof(train.type), MSG_NOSIGNAL);
        send(netfd, &train.length, sizeof(train.length), MSG_NOSIGNAL);
        send(netfd, train.value, train.length, MSG_NOSIGNAL);

        UsrInfoInit(UsrInfo,user_name); //用户名存在，初始化用户信息
    }
    else // 用户名已存在
    {
        memset(&train, 0, sizeof(train));
        train.type = RESPON_ERROR;
        char *msg = "用户名已存在！";
        train.length = strlen(msg);
        send(netfd, &train.type, sizeof(train.type), MSG_NOSIGNAL);
        send(netfd, &train.length, sizeof(train.length), MSG_NOSIGNAL);
        memcpy(train.value, msg, train.length);
        send(netfd, train.value, train.length, MSG_NOSIGNAL);
        // 记录注册失败日志
        pthread_mutex_lock(&pThreadPool->mutex);
        LOG_INFO(pThreadPool->log_fd, LOG_SIGNUP, "username %s signup is already existed.", user_name);
        pthread_mutex_unlock(&pThreadPool->mutex);
        return -1;
    }

    mysql_free_result(rows);

    // 生成盐值
    char salt[21] = {0};
    int salt_ret = generate_salt(salt, sizeof(salt));
    if(salt_ret == 0)
    {
        printf("get salt OK. salt: %s\n", salt);
    }

    // 发送盐值
    train.length = strlen(salt);
    send(netfd, &train.length, sizeof(train.length), MSG_NOSIGNAL);
    send(netfd, salt, train.length, MSG_NOSIGNAL);

    // 接收加密密码
    char encrypted_psw[65] = {0};
    memset(&train, 0, sizeof(train));
    recv(netfd, &train.length, sizeof(train.length), MSG_WAITALL);
    recv(netfd, encrypted_psw, train.length, MSG_WAITALL);
   
    // 用户信息插入数据库
    memset(sql, 0, sizeof(sql));
    sprintf(sql, "insert into user_info (username,encrypted_password, salt) values ('%s','%s','%s')", user_name, encrypted_psw, salt);
    qret = mysql_query(mysql, sql);
    if(qret != 0)
    {
        pthread_mutex_lock(&pThreadPool->mutex);
        LOG_INFO(pThreadPool->log_fd, LOG_ERROR, "mysql_query failed. %s", mysql_error(mysql));
        pthread_mutex_unlock(&pThreadPool->mutex);
        train.type = RESPON_ERROR;
        send(netfd, &train.type, sizeof(train.type), MSG_NOSIGNAL);
        return -1;
    }

    // 为用户创建根目录
    memset(sql, 0, sizeof(sql));
    sprintf(sql, "INSERT INTO file_info (filename, username, parent_id, file_path,type, is_del) VALUES ('/', '%s', -1, '/', 'DIR', 0);", user_name);
    qret = mysql_query(mysql, sql);
    if(qret != 0)
    {
        pthread_mutex_lock(&pThreadPool->mutex);
        LOG_INFO(pThreadPool->log_fd, LOG_ERROR, "mysql_query failed. %s", mysql_error(mysql));
        pthread_mutex_unlock(&pThreadPool->mutex);
        train.type = RESPON_ERROR;
        send(netfd, &train.type, sizeof(train.type), MSG_NOSIGNAL);
        return -1;
    }

    // 返回确认
    train.type = RESPON_OK;
    send(netfd, &train.type, sizeof(train.type), MSG_NOSIGNAL);

    // 记录注册成功日志
    pthread_mutex_lock(&pThreadPool->mutex);
    LOG_INFO(pThreadPool->log_fd, LOG_SIGNUP, "user %s signup successful.", user_name);
    pthread_mutex_unlock(&pThreadPool->mutex);

    return 0;
}

int generate_salt(char *salt, int salt_size)
{
    srand(time(NULL));
    if(salt == NULL || salt_size < 21)
    {
        return -1;
    }
    memset(salt, 0, salt_size);
    char random_char[]= "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./";
    strcat(salt, "$6$");
    for(int i = 0; i < 16; ++i)
    {
        char temp[2] = {0};
        temp[0] = random_char[rand()%64];
        strcat(salt, temp);
    }
    strcat(salt, "$");
    return 0;
}